﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ScFramework.Core;
using System;
using System.Reflection;
using ScFramework.Utilities;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif

public static class EntityUtilities
{

   /* public class ValueSetting
    {
        public object DefaultValue;
        public bool IsPureValue;
    }*/
  //  public static IValueChangingChecker ValueChecker;


    //static Dictionary<Type, UInt32> typeHashDic = new Dictionary<Type, UInt32>();
    static Dictionary<Tuple<Type, string>, UInt16> valueNameDic = new Dictionary<Tuple<Type, string>,UInt16>();
    static Dictionary<Tuple<Type, UInt16>, string> valueIdDic = new Dictionary<Tuple<Type, UInt16>,string>();


#if UNITY_EDITOR
    [UnityEditor.Callbacks.DidReloadScripts(1)]
#else
    [RuntimeInitializeOnLoadMethod]
#endif
    public static void InitUseableComponent()
    {
        List<Type> useableType = new List<string>(TypeUtilities.GetTypeNames(typeof(SComponent))).ConvertAll(s => Type.GetType(s));
        valueNameDic.Clear();
       // typeHashDic.Clear();
        valueIdDic.Clear();

      // defaultValueDic.Clear();
     //   UInt32 cnt = 0;
        foreach (var x in useableType)
        {
            ///0值不作为哈希值
            //         typeHashDic[x] = ++cnt;
            SComponent.TravelSyncValue(x, (FieldInfo v, ushort idCnt) =>
             {

                 valueIdDic[new Tuple<Type, ushort>(x, idCnt)] = v.Name;
                 valueNameDic[new Tuple<Type, string>(x, v.Name)] = idCnt;
             }, (PropertyInfo v, ushort idCnt) =>
             {

                 valueIdDic[new Tuple<Type, ushort>(x, idCnt)] = v.Name;
                 valueNameDic[new Tuple<Type, string>(x, v.Name)] = idCnt;
             });
        

        }

    }


    public static List<GameObject> entityPrefabs;


    static Dictionary<ushort, List<List<ushort>>> colliderConfigDic = null;
    public static List<List<ushort>>GetColliderUnionConfig(ushort eid)
    {
        return null;
    }
    static void SetColliderConfig(GameObject obj)
    {
        if(obj.GetComponent<SComBody>() is SComBody body)
        {
            if (body.ColliderRootConfig != null && body.ColliderRootConfig.Count > 0)
            {
                //List<HashSet<ushort>> config = new List<HashSet<ushort>>();
                Stack<Tuple<GameObject, HashSet<ushort>>> stk = new Stack<Tuple<GameObject, HashSet<ushort>>>();
                colliderConfigDic[body.Entity.PrefabId] = new List<List<ushort>>();
                dfs(obj,stk,body.ColliderRootConfig, colliderConfigDic[body.Entity.PrefabId]);
            }
        }
    }
    static void dfs(GameObject gameObject,Stack<Tuple<GameObject, HashSet<ushort>>> stk,List<GameObject>config,List<List<ushort>>output)
    {
        if (stk.Count == 0||config.Contains(gameObject) )
        {
            stk.Push(new Tuple<GameObject, HashSet<ushort>>(gameObject, new HashSet<ushort>()));
        }
        foreach(var x in gameObject.GetComponents<SComponent>())
        {
            stk.Peek().Item2.Add(x.SData.CompIdentity.right());
        }
        for(int i = 0; i < gameObject.transform.childCount; i++)
        {
            dfs(gameObject.transform.GetChild(i).gameObject, stk, config, output);
        }
        if (stk.Peek().Item1 == gameObject)
        {
            output.Add(stk.Peek().Item2.ToList());
            stk.Pop();
        }
    }



    static Dictionary<string, UInt16> nameToIdDic;


    static Dictionary<UInt64, object> defaultValueDic;

    static Dictionary<UInt64, Type> valueTypeDic;


    static Dictionary<Type, ushort> compValueCnt;

    public static ushort GetValueCnt(Type type)
    {
        Debug.Assert(typeof(SComponent).IsAssignableFrom(type));
        return compValueCnt[type];
    }

    public static string GetEntityName(UInt16 id)
    {
        return entityPrefabs[id].name;
    }
    public static Type GetValueType(UInt32 compId,UInt16 valueId)
    {
        var code = ((UInt64)compId) << 16 | valueId;

        return valueTypeDic[code];
    }
    public static T GetDefaultValue<T>(UInt32 compId,UInt16 valueId)
    {
        var code = ((UInt64)compId) << 16 | valueId;

     //   Debug.Log(compId + " " + valueId);

        if (defaultValueDic.ContainsKey(code))
        {
        //    Debug.Log(typeof(T) + " " + defaultValueDic[code].GetType());
            return (T)defaultValueDic[code];
        }
        else
        {
            return (T)ValueUtil.Get<T>().DefaultValue;
        }
    }


    public static object GetDefaultValue(Type type,UInt32 compId, UInt16 valueId)
    {
        var code = ((UInt64)compId) << 16 | valueId;

        //   Debug.Log(compId + " " + valueId);

        if (defaultValueDic.ContainsKey(code))
        {
       //     Debug.Log(defaultValueDic[code].GetType() + "|" + type);
            Debug.Assert(defaultValueDic[code].GetType() == type);
            //    Debug.Log(typeof(T) + " " + defaultValueDic[code].GetType());
            return defaultValueDic[code];
        }
        else
        {
            return ValueUtil.Get(type).DefaultValue;
        }
    }
    public static GameObject GetPrefab(UInt16 id)
    {
        return entityPrefabs[id];
    }
  //  [RuntimeInitializeOnLoadMethod]
    public static void InitEntityDefaultSetting()
    {
        entityPrefabs = new List<GameObject>();
        nameToIdDic = new Dictionary<string, ushort>();
        defaultValueDic = new Dictionary<ulong, object>();
        valueTypeDic = new Dictionary<ulong, Type>();
        compValueCnt = new Dictionary<Type, ushort>();

        colliderConfigDic = new Dictionary<ushort, List<List<ushort>>>();
        //     prefabsDic = new Dictionary<string, GameObject>();
        //load EntityPrefab from file or data
  //      var ss = new List<GameObject> (Resources.LoadAll<GameObject>("Prefab/Item/"));
        entityPrefabs = new List<GameObject>(Resources.LoadAll<GameObject>("Prefab/Item/"));

        for (UInt16 i = 0; i < entityPrefabs.Count; i++)
        {
            nameToIdDic[entityPrefabs[i].name] = i;
            SetColliderConfig(entityPrefabs[i]);
      //      Debug.Log("name=" + entityPrefabs[i].name + "\n--------------------");
            var complist = entityPrefabs[i].GetAll<SComponent>();
            CreateEntityAndInit(entityPrefabs[i], parent: entityPrefabs[i].transform.parent);
            for (UInt16 j = 0; j < complist.Count; j++)
            {
                if(complist[j] is SComBody e)
                {

                }
                UInt64 code = i;
                code <<= 32;
                code |= ((UInt64)j) << 16;

                compValueCnt[complist[j].GetType()] = complist[j].ValueCnt;
                for (UInt16 k = 0; k < complist[j].ValueCnt; k++)
                {
                    if (complist[j].IsValuePure(k) == false)
                    {
                        if (ValueUtil.EqualsDefault(complist[j].GetInstanceValue(k)) == false)
                        {

                            valueTypeDic[code | k] = complist[j].GetInstanceValue(k).GetType();
                            if (ValueUtil.EqualsDefault(complist[j].GetInstanceValue(k)) == false)
                            {
                                defaultValueDic[code | k] = complist[j].GetInstanceValue(k);
                            }
               //             Debug.Log($"vid={j} {k},v={complist[j].GetInstanceValue(k)}");
                        }
                    }
                }
                SComponent.TravelSyncValue(complist[j].GetType(), (FieldInfo v, ushort cnt) => {
                    if (v.GetValue(complist[j]) == null)
                    {
                        Debug.Log(v);
                    }
                    valueTypeDic[code | cnt] = v.GetValue(complist[j]).GetType();
                    if (ValueUtil.EqualsDefault(v.GetValue(complist[j])) == false)
                    {
                        defaultValueDic[code | cnt] = v.GetValue(complist[j]);
                    }
                
                }, null);
            }

        }
    }


    public static UInt16 GetIdByName(string name)
    {
        //Debug.Log(name);
        return nameToIdDic[name];
    }

    /// <summary>
    /// 根据变量名称和所属组件获取其在该组件中的id
    /// </summary>
    /// <param name="c"></param>
    /// <param name="name"></param>
    /// <returns></returns>
    public static UInt16 GetValueID(ComponentData c, string name)//where T:SComponent
    {
        return GetValueID(c.ComponentType, name);
    }
    public static UInt16 GetValueID(Type componentType, string name)//where T:SComponent
    {
      
        return valueNameDic[new Tuple<Type, string>(componentType, name)];
    }
    public static string GetValueName(ComponentData c, ushort id)//where T:SComponent
    {
        return GetValueName(c.ComponentType, id);
    }
    public static string GetValueName(Type componentType, ushort id)//where T:SComponent
    {
        return valueIdDic[new Tuple<Type, ushort>(componentType, id)];
    }
    /* public static object GetDefaultValue(Type type, int id)
     {
         Debug.Assert(typeof(SComponent).IsAssignableFrom(type));
         return defaultValueDic[type][id];
     }*/

    /*public static object GetDefaultValue<T>(int id) where T : SComponent
    {
        return GetDefaultValue(typeof(T), id);
    }*/
  /*  public static UInt32 GetTypeHash(Type type)
    {
        Debug.Assert(typeof(SComponent).IsAssignableFrom(type));
        return typeHashDic[type];
    }*/
    public static Entity CreateEntityAndInit(GameObject obj,UInt32 entityID=0,List<ComponentData> datas=null,Transform parent=null)
    {
        //Debug.Assert(obj.GetAll<SComponent>() == comps.Count);
        var e = new Entity();
        int cnt = 0;
        if (datas == null)
        {
            datas = new List<ComponentData>();
        }
        int dcnt = 0;

        List<ComponentData> ndata = new List<ComponentData>();

        List<SComponent> allComp = obj.GetAll<SComponent>();
        foreach (var c in allComp)
        {
        //    Debug.Assert(x.GetComponents<SGameObject>()!=null);
         //   var sg = x.GetComponents<SGameObject>()
                ComponentData componentData;
                if (dcnt < datas.Count && datas[dcnt].CompIdentity.right() == cnt)
                {
                    componentData = datas[dcnt++];
                }
                else
                {
                    componentData = new ComponentData();
                componentData.CompIdentity = entityID.right().combine((ushort)cnt);
                componentData.EntityPrefabID = entityID.left();
             //       componentData.Identity = (ushort)cnt;
              //      componentData.EntityIdentity = entityID;
                    componentData.ComponentType = c.GetType();
                }

                ndata.Add(componentData);
         //       Debug.Log(componentData.ComponentType + " " + c.GetType());
                Debug.Assert(componentData.CompIdentity.right() ==(ushort)cnt&& componentData.ComponentType == c.GetType());



                c.SBindID(e, componentData);
             
                cnt++;
            }
        obj.transform.SetParent(parent);
        datas.Clear();
      //  e.gameObject = obj;
        foreach(var x in ndata)
        {
            datas.Add(x);
        }
        e.Init(allComp,obj);
        return e;
    }

}
/*public interface IValueChangingChecker
{   /// <summary>
    /// 检测是否能修改
    /// </summary>
    /// <param name="id"></param>
    /// <param name="isPure"></param>
    /// <returns></returns>
    bool CheckIfCanModify(UInt16 id,bool isPure);
    /// <summary>
    /// 检测是否需要同步数据,并生成同步指令
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="id"></param>
    /// <param name="oldValue"></param>
    /// <param name="newValue"></param>
    /// <returns></returns>
    bool CheckIfNeedSync<T>(UInt16 id, T oldValue,T newValue);

    void DispathValueChangedMessage<T>(UInt32 id, UInt16 valueID,T newValue) ;

}*/